package org.datascooter.bundle;

import org.datascooter.bundle.attribute.ContainerAttribute;
import org.datascooter.bundle.attribute.DBIndex;
import org.datascooter.bundle.attribute.FieldContainerAttribute;
import org.datascooter.bundle.attribute.FieldReference;
import org.datascooter.bundle.attribute.MethodContainerAttribute;
import org.datascooter.bundle.attribute.MethodReference;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.bundle.attribute.PersistChildAttribute;
import org.datascooter.bundle.attribute.PersistChildFieldAttribute;
import org.datascooter.bundle.attribute.PersistChildMethodAttribute;
import org.datascooter.bundle.attribute.PersistFieldAttribute;
import org.datascooter.bundle.attribute.PersistMethodAttribute;
import org.datascooter.bundle.attribute.PersistReferenceAttribute;
import org.datascooter.bundle.attribute.PersistencePolicy;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.utils.DBType;
import org.datascooter.utils.DSSettings;

public class BundleFactory {

	private static boolean USE_FIELD_ATTRIBUTES = false;
	private static EntityBundle bundle;

	public static EntityBundle create(String name, String schema, String table, String klass, boolean uncheckable, boolean dbSchema) {
		bundle = new EntityBundle(name, schema, table, klass, uncheckable, dbSchema);
		USE_FIELD_ATTRIBUTES = DSSettings.isUseFieldAttributes();
		return bundle;
	}

	/**
	 * Completes a creation of bundle with verifying table (checking or creation if not exists)
	 * 
	 * @throws EntityNotMappedException
	 */
	public static void complete() throws EntityNotMappedException {
		DSMapper.addBundle(bundle.entity, bundle);
		bundle = null;
	}

	/**
	 * Completes a creation of bundle but without verifying table (checking or creation if not exists)
	 * 
	 * @throws EntityNotMappedException
	 */
	public static void completeSilent() throws EntityNotMappedException {
		DSMapper.addBundleInt(bundle);
		bundle = null;
	}

	public static void addIndex(String name, String type, PersistAttribute... attrs) {
		bundle.addIndex(new DBIndex(name, type, attrs));
	}

	public static PersistAttribute createDefaultId() throws SecurityException, NoSuchMethodException {
		return createId("id", "ID", DBType.CHAR, 36, 0, true, true, false);
	}

	public static PersistAttribute createId(String name, String column, DBType type, int scale, int precision, boolean insertable, boolean updatable, boolean nullable) throws SecurityException, NoSuchMethodException {
		PersistAttribute attribute;
		if (USE_FIELD_ATTRIBUTES) {
			attribute = new PersistFieldAttribute(name, column, type, scale, precision, insertable, updatable, nullable);
		} else {
			attribute = new PersistMethodAttribute(name, column, type, scale, precision, insertable, updatable, nullable);
		}
		if (bundle != null)
			bundle.setId(attribute);
		return attribute;
	}

	public static PersistAttribute createFieldId(String name, String column, DBType type, int scale, int precision, boolean insertable, boolean updatable, boolean nullable) throws SecurityException, NoSuchMethodException {
		PersistAttribute attribute = new PersistFieldAttribute(name, column, type, scale, precision, insertable, updatable, nullable);
		if (bundle != null)
			bundle.setId(attribute);
		return attribute;
	}

	public static PersistAttribute createPersist(String name, String column, DBType type, int scale, int precision, boolean insertable, boolean updatable, boolean nullable) throws SecurityException, NoSuchMethodException {
		PersistAttribute attribute;
		if (USE_FIELD_ATTRIBUTES) {
			attribute = new PersistFieldAttribute(name, column, type, scale, precision, insertable, updatable, nullable);
		} else {
			attribute = new PersistMethodAttribute(name, column, type, scale, precision, insertable, updatable, nullable);
		}
		if (bundle != null)
			bundle.addPersistAttribute(attribute);
		return attribute;
	}

	public static PersistReferenceAttribute createReference(
		String name,
		String entityName,
		String column,
		DBType type,
		int scale,
		int precision,
		boolean insertable,
		boolean updatable,
		boolean nullable) throws SecurityException, NoSuchMethodException {
		PersistReferenceAttribute attribute;
		if (USE_FIELD_ATTRIBUTES) {
			attribute = new FieldReference(name, entityName, column, type, scale, precision, insertable, updatable, nullable);
		} else {
			attribute = new MethodReference(name, entityName, column, type, scale, precision, insertable, updatable, nullable);
		}
		if (bundle != null) {
			bundle.addReferenceAttribute(attribute);
			bundle.addIndex(new DBIndex(column, null, attribute));
		}
		return attribute;
	}

	public static ContainerAttribute createContainer(String name, String entityName, String mapKey, PersistencePolicy policy, boolean embedded, boolean fetchLazy) throws SecurityException, NoSuchMethodException {
		ContainerAttribute attribute;
		if (USE_FIELD_ATTRIBUTES) {
			attribute = new FieldContainerAttribute(name, entityName, mapKey, policy, embedded, fetchLazy);
		} else {
			attribute = new MethodContainerAttribute(name, entityName, mapKey, policy, embedded, fetchLazy);
		}
		if (bundle != null)
			bundle.addContainerAttribute(attribute);
		return attribute;
	}

	public static PersistChildAttribute createPersistChild(
		String name,
		String entity,
		String mapKey,
		PersistencePolicy policy,
		boolean embedded,
		boolean fetchLazy,
		String joinAttribute,
		boolean joinFromChild) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		PersistChildAttribute attribute;
		if (USE_FIELD_ATTRIBUTES) {
			attribute = new PersistChildFieldAttribute(name, entity, mapKey, policy, embedded, fetchLazy, joinAttribute, joinFromChild);
		} else {
			attribute = new PersistChildMethodAttribute(name, entity, mapKey, policy, embedded, fetchLazy, joinAttribute, joinFromChild);
		}
		if (bundle != null)
			bundle.addPersistableChild(attribute);
		return attribute;
	}

	public static void setUseFieldAttributes(boolean value) {
		USE_FIELD_ATTRIBUTES = value;
	}
}
